/*
事务
对于一组执行的代码，如果一个发生了错误，那么他的整个执行都要停止
如果发生了停止，那么整个数据库都要回到开始事务之前的状态
对于事务，可以直接结束，然后数据库的状态就会更新新的数据
也可以回溯，即使成功执行之后，也可以回到开始之前的状态
*/

-- 语法
create table bank_count(
id bigint primary key auto_increment,
name varchar(20),  # 名字 
balance decimal(10,2) # 数额
);

INSERT INTO bank_count(name, balance) VALUES('张三', 1000);
INSERT INTO bank_count(name, balance) VALUES('李四', 1000);

-- 使用 start transaction
start transaction;

-- 这下面就是事务的具体代码

update bank_count set balance = balance - 100 where name = '张三';

-- 若没有commit推出事务，那么数据是没有写道数据库中的
commit;

-- 使用begin开始一个事务

begin;

insert into bank_count(name,balance) values ('王五',2000);

select * from bank_count; -- 这里能看到新加入的数据 但是实际没有写入数据库中

update bank_count set balance = balance - 200 where name = '王五';

-- 回溯 begin end 是对 start contraction  commit是一对
end;

select * from bank_count; -- 并没有更新数据

-- 可以在指定点设置保存点， 这样就可以回到保存点对应储存的数据 保存点要在事务中使用
-- savepoint point_name

delete from bank_count where name = '王五';

select * from bank_count;

start transaction;
-- 设置保存点1
savepoint point1;
-- 插入 数据王五
insert into bank_count(name,balance) values ('王五',2000);
-- 保存点2
savepoint point2;
-- 回滚到保存点1
rollback to point1;

select * from bank_count;

-- 回滚到保存点1之后1之前的保存点都没有了，也就是不能再回到回退之前了
rollback to point2;

rollback;

-- 自动提交事务 其实每次的炒作都会自动创建一个事务，当他发生错误时会自动回滚

-- 查看是否为自动提交 on 是
show variables like 'autocommit';

-- 也可以修改
-- 关,那么再平时的操作中就可以自己使用rollback回退，不需要使用start transaction
set autocommit = 0;
set autocommit = off;
-- 自动提交
set autocommit = 1;
set autocommit = on;

select * from bank_count;

savepoint point1;

insert into bank_count(name,balance) values ('hh',1234);

savepoint point2;

rollback to point1;

delete from bank_count where name = 'hhh';


/*
访问权限
事务的隔离性 mysql可以被多个客户端同时访问，每个客户端执行的dml(删除，更新，插入)语句是以
事务为单位的，如果俩个客户端对同一条数据进行修改，那么就可能导致数据信息的错误，mysql
就需要拒绝这样的影响发生，那么事务之间就需要相互隔离性，这就体现了事务的隔离性
*/

/*
隔离级别 隔离界别越高，性能越低，级别越低，性能越高
• READ UNCOMMITTED ，读未提交  存在的安全问题		脏读  可重复读   幻读
	若有a事务在访问数据c并对他进行了修改，但是没有提交，这时b事务又访问数据c，那么这么事务b得到的
	数据c是a未提交的数据，这种情况叫做脏读，b事务读的是a事务的rollback的数据
• READ COMMITTED ，读已提交				可重复读	幻读
	若b再读取数据c的时候，a也再读取数据c并对c进行了修改并提交了，这时b并没有推出事务，b再对c进行查询
	发现数据c发生了变化变成a修改后的数据，这时b俩次查询到的结果不相同，这种现象叫做不可重复读
• REPEATABLE READ ，可重复读(默认) innodb默认隔离级别			幻读
	在a查询一个结果集d中的一条数据c后，会将c'上锁'这样就不可以其他事务在对数据c进行修改，但是允许对
	结果集其他事务进行修改，若这是有b去对结果集进行插入或删除操作，会发现俩次结果集的结果不同
	这种现象就叫做幻读
• SERIALIZABLE，串行化		全部都解决了
	是最安全的隔离级别，所有事务都是一个挨一个的对数据进行操作，每个事务都不需要等
	前一个结束了才可以进行操作，解决了所有的安全问题
*/
-- 全局作用域
SELECT @@GLOBAL.transaction_isolation;
-- 会话作用域
select @@session.transaction_isolation;

-- 设置会话作用域为 READ UNCOMMITTED 读未提交 脏读

set global transaction isolation level READ UNCOMMITTED;

set session transaction isolation level REPEATABLE READ;
	-- 这里面的隔离级别是REPEATABLE READ
end;

select * FROM bank_count;

delete from bank_count where name in('halo','wow');
-- 现在的全局隔离级别是READ UNCOMMITTED 读未提交（脏读）在另一个事务中读到了一个事务中未提交的数据
-- 如果这个事务回滚以后那么这个数据是无效的
begin;

insert into bank_count(name,balance) values ('halo',3456);

rollback;

commit;

end;

-- READ COMMITTED 读已提交 只能读到提交之后的数据
-- 在这里读取数据 然后另一个事务修改了一条数据并提交之后，这里的这条数据发生了变化
-- 也就是在一个事务中同样的俩次查询，数据结果不一样
select @@global.transaction_isolation;

set global transaction isolation level READ COMMITTED;

start transaction;

insert into bank_count(name,balance) values ('wow',4567);

update bank_count set balance = 6544 where name = '李四';

select * from bank_count;
 
commit;

rollback;

-- REPEATABLE READ 
select @@global.transaction_isolation;

set global transaction isolation level REPEATABLE READ;

start transaction;

--  不能插入 mysql不只会锁定其他事务使用的数据，还会锁定数据之间的上下链接让数据无法插入或修改
insert into bank_count(id,name,balance) VALUES(28,'ww',333);

commit;


-- 串行化 数据只能一个事务接着一个事务的对数据进行修改
set global transaction isolation level SERIALIZABLE;


start transaction;

select * from bank_count;

-- 无法插入 等另一个事务完成了就能插入了
update bank_count set balance = 2121 where name = 'ww';














